use anyhow::{Result, anyhow};
use serde::Deserialize;

// T 代表 data 字段里的具体类型
#[derive(Debug, Clone, Deserialize, serde::Serialize)]
pub struct ApiResponse<T> {
    pub code: i32,
    pub message: String,
    // data 是泛型 T，且可能是 null (Option)
    pub data: Option<T>,
}

impl<T> ApiResponse<T> {
    /// 辅助方法：解包 API 响应
    /// 如果 code != 0 或 data 为空，返回 Err
    /// 如果成功，返回 T
    pub fn into_result(self) -> Result<T> {
        if self.code != 0 {
            return Err(anyhow!("API Error: code={}, msg={}", self.code, self.message));
        }

        // 如果 code 是 0，但 data 是 None，通常也视作一种错误（或者根据业务逻辑处理）
        self.data
            .ok_or_else(|| anyhow!("API returned success but data is missing"))
    }
}

use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;

pub async fn calculate_file_md5(path: &Path) -> anyhow::Result<String> {
    let path = path.to_path_buf();

    // 放入 spawn_blocking 防止阻塞异步线程
    let digest = tokio::task::spawn_blocking(move || {
        let file = File::open(path)?;
        // 使用 BufReader 减少系统调用次数，buffer 设为 8KB - 64KB 通常性能最好
        let mut context = md5::Context::new();
        let mut reader = BufReader::with_capacity(1024 * 1024, file); // Reader 缓冲 1MB
        let mut buffer = vec![0; 1024 * 1024]; // 读取 buffer 1MB

        loop {
            let count = reader.read(&mut buffer)?;
            if count == 0 {
                break;
            }
            context.consume(&buffer[..count]);
        }

        Ok::<_, anyhow::Error>(context.finalize())
    })
    .await??;

    Ok(format!("{:x}", digest))
}
